#include "game.h"

#include "basecode\bitmap_font.h"
#include <glm/glm.hpp>
#include <vector>
#include <algorithm>
#include "World.h"

#include "bullet.h"
std::vector< Bullet* > liveBulletList;
std::vector< Bullet* > deadBulletList;



Game::Game() : GLFWApp()
{
	BitmapFont::Init();
	Input::Init();

	world1 = new World(this);

	int maxbullets = 1000;
	for(int i = 0; i<maxbullets; i++ )
	{
		deadBulletList.push_back( new Bullet() );
	}
	//liveBulletList.resize( maxbullets );

	mainloop();
}

Game::~Game()
{
}

void Game::update()
{
	glm::vec2 mousePos = glm::vec2( Input::mouseX() ,  Input::mouseY() );

	static double shotCooldown = 0;
	shotCooldown -= dt;

	bool shootKey = Input::keyDown(GLFW_KEY_LCTRL) || Input::keyDown(GLFW_KEY_RCTRL);

	if( shootKey)
	{
		if( deadBulletList.size() > 0 && shotCooldown <= 0.0 )
		{
			Bullet* bulletPtr = deadBulletList.back();
			deadBulletList.pop_back();

			
			glm::vec2 dir = glm::normalize(mousePos - playerpos);
			bulletPtr->shoot( playerpos, dir, 5.0 );
			liveBulletList.push_back( bulletPtr );
			shotCooldown = 0.1;
			
		}
	}

	if( Input::MouseDown(GLFW_MOUSE_BUTTON_MIDDLE) )
	{
		playerpos = mousePos;
	}
}

double lerp(double x1,double x2,double t){
	return (x2-x1)*t;
}


void Game::render()
{
	float scale = world1->getScale();
	int SIZ = world1->getSize();
	static float ballradi = 6;
	
	static glm::vec2 acceleration(0,0);
	static glm::vec2 velocity(0,0); 
	static bool canJump = false;

	float vx = (float) (Input::keyDown(GLFW_KEY_RIGHT) - Input::keyDown(GLFW_KEY_LEFT) ) * 1 * (float)dt;
	float vy = (float) (Input::keyDown(GLFW_KEY_DOWN) - Input::keyDown(GLFW_KEY_UP) ) * 1 * (float)dt;
	float jumpForce = (float) (Input::keyDown(GLFW_KEY_SPACE)) * 4*120;
	if( !canJump )
	{
		jumpForce = 0;
	}else
	{
		if( jumpForce > 0 ){
			vy -= 1000 * (float)dt;
			acceleration += glm::vec2(0,-jumpForce / 1.0f);
		}
	}
	static glm::vec2 oldplayerpos(20,20);
	oldplayerpos = playerpos;

	velocity += glm::vec2( vx, vy );
	velocity = 100.0f*glm::vec2( vx, vy );
	acceleration += glm::vec2(0,120);

	//velocity += acceleration * (float)dt;>
	playerpos += velocity;
	acceleration = glm::vec2(0,0);
	velocity *= 0.998; // dampening
	//if( fabs(vx) == 0 ) velocity[0] *= 0.5;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	//glfwSwapInterval(0);

	setOrthoProjection();

	glDisable(GL_LIGHTING);
	glDisable(GL_DEPTH_TEST);


	glm::vec2 closestPoint;
	glm::vec2 surfaceNormal;
	bool closestDistValid;
	world1->getClosestPointAndNormal( playerpos , ballradi, closestPoint, surfaceNormal, closestDistValid  );
	
	float speed = glm::length(velocity);
	float dpr = 0;

	if (closestDistValid)
	{
		dpr = glm::dot( glm::normalize(velocity), surfaceNormal );
		float distance = glm::length( playerpos - closestPoint );


		if( distance <= ballradi )
		{
			float diff = distance - ballradi;
			playerpos = (playerpos) - surfaceNormal * diff;

			if (speed > 100.0f) // only bounce on high speed impact
			{
				// reflect velocity off normal
				velocity = glm::reflect( velocity, surfaceNormal ); // R=I-2*N*D
				velocity *= 0.75f; // velocity is lost on impact
			}else{
				velocity += surfaceNormal * speed; // don't let velocity accumulate when colliding
			}
		}
		
		// TODO find better solution for allowing jumping -> Only allow jumping when player has ground under his feet. Ie surface normal is pointing in upwards direction
		if (distance <= ballradi+2 && surfaceNormal[1] < 0 && fabs(surfaceNormal[0]) < 0.7f ) 
		{
			canJump = true;
		}else
		{
			canJump = false;
		}

	}

	world1->draw();
	//world1->drawNormals();

	for( size_t i=0; i<liveBulletList.size(); i++ )
	{
		liveBulletList[i]->update(dt);
		liveBulletList[i]->draw();

		glm::vec2 closestPoint;
		glm::vec2 surfaceNormal;
		bool closestDistValid;
		int radi = 4;
		world1->getClosestPointAndNormal( liveBulletList[i]->pos , radi, closestPoint, surfaceNormal, closestDistValid  );

		if (closestDistValid)
		{
			float distance = glm::length( liveBulletList[i]->pos - closestPoint );
			if( distance <= radi )
			{
				float diff = distance - radi;
				//glm::vec2 hitPos = (liveBulletList[i]->pos) - surfaceNormal * diff;
				glm::vec2 hitPos = closestPoint + surfaceNormal;

				int xp = (int) hitPos[0];
				int yp = (int) hitPos[1];
				if (xp > 1 && xp < (SIZ-1) && yp > 1 && yp < (SIZ-1) )
				{
					world1->paintbrush(xp, yp, 1, radi*2); // add
					//world1->paintbrush(xp, yp, 0, brushSiz); // remove
					world1->regenRegion(xp,yp,radi*4); 
				}

				liveBulletList[i]->setFinished();
			}
		}
	}
	for( size_t i=0; i < liveBulletList.size(); i++ )
	{
		if( Bullet::canRemove( liveBulletList[i] ) )
		{
			
			deadBulletList.push_back( liveBulletList[i] );	
			
			liveBulletList[i] = liveBulletList.back();
			liveBulletList.pop_back();
		}
	}
	//std::remove_if( liveBulletList.begin(), liveBulletList.end(), Bullet::canRemove );

	glm::vec2 testPoint( Input::mouseX() / scale, Input::mouseY() / scale );
	world1->getClosestPointAndNormal( testPoint, 6, closestPoint, surfaceNormal, closestDistValid );
	if (closestDistValid)
	{
		glBegin(GL_LINES);
		glVertex2i( Input::mouseX(), Input::mouseY()  );
		glVertex2f( closestPoint[0]*scale, closestPoint[1]*scale );
		glEnd();
	}


	if( canJump )
	{
		glColor3f( 1, 0, 0);
	}else{
		glColor3f( 1, 1, 1);
	}
	DrawCircle2D( playerpos[0] * scale, playerpos[1] * scale, ballradi * scale, true);

	static int oldmousx, oldmousy; // Used for interpolation of paint brush strokes

	// Paint/remove earth
	if( Input::MouseDown(GLFW_MOUSE_BUTTON_LEFT) || Input::MouseDown(GLFW_MOUSE_BUTTON_RIGHT) )
	{		

		double scaledMx = Input::mouseX()/scale;
		double scaledMy = Input::mouseY()/scale;
		double scaledOldMx = oldmousx/scale;
		double scaledOldMy = oldmousy/scale;
		int segs = 5;
		int brushSiz = 10;
		for(int i=0; i<segs; i++)
		{
			int xp = int( scaledMx+lerp(scaledMx,scaledMx,i/segs) );
			int yp = int( scaledMy+lerp(scaledOldMy,scaledMy,i/segs) );
			if (xp > 1 && xp < (SIZ-1) && yp > 1 && yp < (SIZ-1) )
			{
				if ( Input::MouseDown(GLFW_MOUSE_BUTTON_LEFT) ) world1->paintbrush(xp, yp, 1, brushSiz); // add
				if ( Input::MouseDown(GLFW_MOUSE_BUTTON_RIGHT)) world1->paintbrush(xp, yp, 0, brushSiz); // remove
				world1->regenRegion(xp,yp,brushSiz*2); 
			}

		}
	}

	char strBuf[200];
	sprintf_s(strBuf,200,"speed = %.2f", speed );
	BitmapFont::Print(strBuf,15,15,0xffffff);
	sprintf_s(strBuf,200,"dpr = %.2f", dpr );
	BitmapFont::Print(strBuf,15,15*2,0xffffff);
	sprintf_s(strBuf,200,"fps = %.1f", 1.0 / dt );
	BitmapFont::Print(strBuf,15,15*3,0xff0000);

	

	oldmousx = Input::mouseX();
	oldmousy = Input::mouseY();
}

